package cate.game.pvp.eliterace.simulator;

import cate.game.framework.advanced.AdvancedByteAttributes;
import cate.game.framework.advanced.AdvancedLongAttributes;
import cate.game.play.proce.play.PlayPO;
import cate.game.pvp.eliterace.data.*;
import cate.game.pvp.eliterace.handler.*;
import cate.game.pvp.eliterace.timer.EliteRaceTimerTask;
import easy.java.practice.errorcode.EcResult;
import lombok.Getter;
import lombok.Setter;
import org.springframework.util.StringUtils;

import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;

import static cate.game.pvp.eliterace.data.EliteRaceKV.OP_SKIP_SEASON;

public abstract class BaseEliteRaceBetSimulator extends EliteRaceSimulator{
    @Getter
    @Setter
    private boolean running = false;

    @Getter
    private final LinkedList<EliteRaceRecord> recordQueue = new LinkedList<>();

    private long currentPowerDiff = Long.MAX_VALUE;
    private EliteRaceRecord betRecord;



    public BaseEliteRaceBetSimulator(Supplier<EliteRaceKV> kvSupplier, Supplier<EliteRaceRecordOperation> recordOperationSupplier, Supplier<EliteRacePsOperation> psOperationSupplier, Supplier<EliteRaceFightOperation> fightOperationSupplier, Supplier<EliteRaceRankRwdOperation> rankRwdOperationSupplier, Supplier<EliteRaceBetOperation> betOperationSupplier, Supplier<EliteRaceVideoOperation> videoOperationSupplier) {
        super(kvSupplier, recordOperationSupplier, psOperationSupplier, fightOperationSupplier, rankRwdOperationSupplier, betOperationSupplier, videoOperationSupplier);
    }

    protected abstract int getPeriodFlag(EliteRaceTimerTask.Settings settings);
    protected abstract List<EliteRaceRecord> query(EliteRaceTimerTask.Settings settings);

    protected void afterRunWith() {

    }

    protected void afterHandleFightTask(EliteRaceRecord recordTask) {

    }

    @Override
    public boolean runWith(EliteRaceTimerTask.Settings settings) {
        AdvancedByteAttributes opAttributes = getOpAttributes();
        AdvancedLongAttributes attributes = getAttributes();
        int periodFlag = getPeriodFlag(settings);
        if (opAttributes.hasAttribute(OP_SKIP_SEASON)) {
            getKV().setSpec(settings.getSpec());
            attributes.setAttribute(periodFlag);
            return true;
        }
        if (attributes.hasAttribute(periodFlag)) {
            return true;
        }

        if (!isRunning()) {
            setRunning(true);
            List<EliteRaceRecord> queryList = query(settings);
            recordQueue.addAll(queryList);
        }

        boolean result = handleQueuedTask();

        if (result) {
            afterRunWith();
            if (Objects.nonNull(betRecord)) {
                betRecord.setBetRound(true);
                EliteRaceBetOperation betOperation = getBetOperation();
                EliteRacePsOperation psOperation = getPsOperation();
                EliteRacePs psA = psOperation.findOne(betRecord.getIdA());
                EliteRacePs psB = psOperation.findOne(betRecord.getIdB());
                betOperation.addRecord(EliteRaceBetRecord.of(betRecord, psA, psB, betRecord.getWinner()));
                betRecord = null;
                currentPowerDiff = Long.MAX_VALUE;
            }
            EliteRaceKV kv = getKV();
            kv.setSpec(settings.getSpec());
            attributes.setAttribute(getPeriodFlag(settings));
        }

        return result;
    }

    private boolean handleQueuedTask() {
        EliteRaceRecord recordTask;
        long startMs = System.currentTimeMillis();
        while ((recordTask = recordQueue.poll()) != null) {
            EcResult<Long> r = handleFightTask(recordTask);
            if (r.ok() && r.data < currentPowerDiff) {
                currentPowerDiff = r.data;
                betRecord = recordTask;
            }
            if (System.currentTimeMillis() - startMs > 500L) {
                break;
            }
        }
        if (recordQueue.isEmpty()) {
            setRunning(false);
        }
        return !isRunning();
    }

    private EcResult<Long> handleFightTask(EliteRaceRecord recordTask) {
        EliteRacePsOperation psOperation = getPsOperation();
        EliteRacePs psA = null, psB = null;
        if (StringUtils.hasLength(recordTask.getIdA())) {
            psA = psOperation.findOne(recordTask.getIdA());
        }
        if (StringUtils.hasLength(recordTask.getIdB())) {
            psB = psOperation.findOne(recordTask.getIdB());
        }
        EliteRaceFightOperation fightOperation = getFightOperation();
        EcResult<Long> r = fightOperation.startFight(
                recordTask,
                psA,
                psB,
                (playPO) -> handleVideo(recordTask.getSpec(), playPO)
        );
        if (!r.ok()) {
            return r;
        }
        afterHandleFightTask(recordTask);
        return r;
    }

    private void handleVideo(EliteRaceSpec spec, PlayPO playPO) {
        EliteRaceVideoOperation videoOperation = getVideoOperation();
        EliteRaceVideo video = EliteRaceVideo.of(playPO.uid, spec, playPO, true);
        videoOperation.upsert(spec, video);
    }
}
